package kvoog.arkanoid.core;

import java.util.Random;


import kvoog.arkanoid.common.EntityBuilder;
import kvoog.arkanoid.common.EntityStateMachine;
import kvoog.arkanoid.core.Health;
import kvoog.arkanoid.core.Player;
import kvoog.arkanoid.physics.BodyBuilder;
import kvoog.arkanoid.physics.CollisionEvent;
import kvoog.arkanoid.physics.Physics;
import kvoog.arkanoid.rendering.Spatial;
import kvoog.arkanoid.rendering.Transform;

import com.artemis.Entity;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Contact;

public class ArkanoidEntityFactory {
	
	private static final short CATEGORY_PAD = 0x0001;
	private static final short CATEGORY_BALL = 0x0002;
	private static final short CATEGORY_WALL = 0x0004;
	private static final short CATEGORY_BRICK = 0x0008;
	private static final short CATEGORY_BOX = 0x0010;
	
	private EntityBuilder entityBuilder;
	private BodyBuilder bodyBuilder;
	
	private static ArkanoidEntityFactory instance;
	
	public static ArkanoidEntityFactory getInstance() {
		if (instance == null) {
			instance = new ArkanoidEntityFactory();
		}
		return instance;
	}
	
	private ArkanoidEntityFactory() {
		
	}
	
	public void setEntityBuilder(EntityBuilder entityBuilder) {
		this.entityBuilder = entityBuilder;
	}
	
	public void setBodyBuilder(BodyBuilder bodyBuilder) {
		this.bodyBuilder = bodyBuilder;
	}
	
	public Entity createPad(float x, float y, float w, float h) {
				
		entityBuilder.reset();
				
		Player player = new Player(0);
		entityBuilder.addComponent(player);
		entityBuilder.addComponent(new Health(3));		
		entityBuilder.addComponent(new Transform(new Vector2(x, y), 0, w, h));
		entityBuilder.addComponent(new Spatial(new String("pad"), /*new Rectangle(0, 0, 91, 31),*/ new Color(Color.WHITE)));
		
		KeyInputReceiver keyInputRec = new KeyInputReceiver();
		keyInputRec.addMapping("MOVE_LEFT", Input.Keys.LEFT, true);
		keyInputRec.addMapping("MOVE_RIGHT", Input.Keys.RIGHT, true);
		//entityBuilder.addComponent(keyInputRec);
		
		Vector2[] vecs = new Vector2[] {
				new Vector2(w/2, -h/2),
				new Vector2(w/2, -h/2+0.8f*h),
				new Vector2(-w/2+2f/3*w, h/2),
				new Vector2(-w/2+1f/3*w, h/2),
				new Vector2(-w/2, -h/2+0.8f*h),
				new Vector2(-w/2, -h/2)
			};
		
		bodyBuilder.reset();
		
		bodyBuilder.type(BodyType.DynamicBody)
				   .position(x, y)
				   .linearDumping(3f)
				   .fixedRotation()
				   .resetFixture()
				       .polygonShape(vecs)
				       .density(1000f)
				       .friction(0f)
				       .restitution(1f)
				       .categoryBits(CATEGORY_PAD)
				       .maskBits((short)(CATEGORY_BALL | CATEGORY_WALL))
				   .addFixture();
		entityBuilder.addComponent(new Physics(bodyBuilder.build()));
		
		entityBuilder.setPlayer("player1"); // ukloniti kasnije
	
		Entity e = entityBuilder.build();
		
		// Proba novih klasa za upravljanje stanjima. Blokira se upravljanje plocicom ako je kraj igre.
		// TODO Integrisati definisanje stanja u EntityBuilder-u.
		EntityStateMachine fsm = new EntityStateMachine(e);
		fsm.createState("playing")
		   .add(KeyInputReceiver.class).withInstance(keyInputRec);
		fsm.createState("gameover");
		player.fsm = fsm;
		
		fsm.changeState("playing");	
		
		return e;
	}
	
	public Entity createBall(float x, float y, float r) {
		
		entityBuilder.reset();
		
		entityBuilder.addComponent(new Ball());
		entityBuilder.addComponent(new Transform(new Vector2(x, y), 0, 2*r, 2*r));
		entityBuilder.addComponent(new Spatial(new String("ball"), /*new Rectangle(0, 0, 56, 54),*/ new Color(Color.WHITE)));
		
		Random rnd = new Random();
		float velx = rnd.nextFloat()/2;
		float vely = 1f;
		Vector2 vel = new Vector2(velx, vely);
		vel.nor();
		vel.mul(3);
		
		bodyBuilder.reset();
		bodyBuilder.type(BodyType.DynamicBody)		
				   .position(x, y)
				   .linearVelocity(vel.x, vel.y)
				   .bullet()
				   .resetFixture()
				       .circleShape(r)
				       .density(1f)
				       .friction(0f)
				       .restitution(1f)
				       .categoryBits(CATEGORY_BALL)
				       .maskBits((short)(CATEGORY_PAD | CATEGORY_WALL | CATEGORY_BRICK | CATEGORY_BOX))
				   .addFixture();
		entityBuilder.addComponent(new Physics(bodyBuilder.build()));
				   
		return entityBuilder.build();
	}
	
	public Entity createBounds(Vector2[] vecs) {

		entityBuilder.reset();
		
		entityBuilder.addComponent(new Bounds());

		bodyBuilder.reset();
		bodyBuilder.type(BodyType.StaticBody)
				   .resetFixture()
				   	   .chainShape(vecs, false)
				   	   .friction(0f)
				   	   .restitution(0f)
				   	   .categoryBits(CATEGORY_WALL)
				   	   .maskBits((short)(CATEGORY_PAD | CATEGORY_BALL))
				   .addFixture();
		entityBuilder.addComponent(new Physics(bodyBuilder.build()));

		return entityBuilder.build();
	}

	public Entity createTrigger(float x, float y, float w, float h) {
		
		entityBuilder.reset();
		
		entityBuilder.addComponent(new Trigger());

		bodyBuilder.reset();
		
		bodyBuilder.type(BodyType.StaticBody)
				   .position(x, y)
				   .resetFixture()
					  .sensor()
				   	  .boxShape(w/2, h/2)
				   	  .friction(0f)
				   	  .restitution(0f)
				   	  .categoryBits(CATEGORY_BOX)
				   	  .maskBits((short)(CATEGORY_BALL))
				  .addFixture();
		entityBuilder.addComponent(new Physics(bodyBuilder.build()));

		return entityBuilder.build();
	}
	
	public Entity createBrick(float x, float y, float w, float h) {
		
		entityBuilder.reset();
		
		entityBuilder.addComponent(new Brick());
		entityBuilder.addComponent(new Transform(new Vector2(x, y), 0, w, h));
		Random rnd = new Random();
		Color color = new Color(rnd.nextFloat()/2+0.25f, rnd.nextFloat()/2+0.25f, rnd.nextFloat()/2+0.25f, 1);
		entityBuilder.addComponent(new Spatial(new String("brick2"), /*new Rectangle(0, 0, 100, 54),*/ color));

		bodyBuilder.reset();
		
		bodyBuilder.type(BodyType.StaticBody)
		           .position(x, y)
				   .resetFixture()
				   	  .boxShape(w/2, h/2)
				   	  .friction(0f)
				   	  .restitution(0f)
				   	  .categoryBits(CATEGORY_BRICK)
				   	  .maskBits((short)(CATEGORY_BALL))
				  .addFixture();
		entityBuilder.addComponent(new Physics(bodyBuilder.build()));

		return entityBuilder.build();
	}
	
	public Entity createCollisionEvent(CollisionEvent.CollisionType type, Contact contact) {
		
		entityBuilder.reset();
		entityBuilder.addComponent(new CollisionEvent(type, contact));		
		
		return entityBuilder.build();
	}
}
